package mcache

import "sync"

const (
	s1K   = 1 << 10
	s2K   = 1 << 11
	s4K   = 1 << 12
	s8K   = 1 << 13
	s16K  = 1 << 14
	s32K  = 1 << 15
	s64K  = 1 << 16
	s128K = 1 << 17
	s256K = 1 << 18
	s512K = 1 << 19
	s1M   = 1 << 20
	s2M   = 1 << 21
	s4M   = 1 << 22
	s8M   = 1 << 23
)

var (
	pool1K   sync.Pool
	pool2K   sync.Pool
	pool4K   sync.Pool
	pool8K   sync.Pool
	pool16K  sync.Pool
	pool32K  sync.Pool
	pool64K  sync.Pool
	pool128K sync.Pool
	pool256K sync.Pool
	pool512K sync.Pool
	pool1M   sync.Pool
	pool2M   sync.Pool
	pool4M   sync.Pool
	pool8M   sync.Pool
)

func mallocByPool(pool *sync.Pool, size int) []byte {
	if v := pool.Get(); v != nil {
		return v.([]byte)
	}
	return make([]byte, size)
}

func malloc(size int) []byte {
	if size <= s1K {
		return mallocByPool(&pool1K, s1K)
	} else if size <= s2K {
		return mallocByPool(&pool2K, s2K)
	} else if size <= s4K {
		return mallocByPool(&pool4K, s4K)
	} else if size <= s8K {
		return mallocByPool(&pool8K, s8K)
	} else if size <= s16K {
		return mallocByPool(&pool16K, s16K)
	} else if size <= s32K {
		return mallocByPool(&pool32K, s32K)
	} else if size <= s64K {
		return mallocByPool(&pool64K, s64K)
	} else if size <= s128K {
		return mallocByPool(&pool128K, s128K)
	} else if size <= s256K {
		return mallocByPool(&pool256K, s256K)
	} else if size <= s512K {
		return mallocByPool(&pool512K, s512K)
	} else if size <= s1M {
		return mallocByPool(&pool1M, s1M)
	} else if size <= s2M {
		return mallocByPool(&pool2M, s2M)
	} else if size <= s4M {
		return mallocByPool(&pool4M, s4M)
	} else if size <= s8M {
		return mallocByPool(&pool8M, s8M)
	}

	return make([]byte, size)
}

func Malloc(size int) []byte {
	return malloc(size)[:size]
}

func Free(bytes []byte) {
	size := cap(bytes)
	bytes = bytes[:size]

	if size == s1K {
		pool1K.Put(bytes)
	} else if size == s2K {
		pool2K.Put(bytes)
	} else if size == s4K {
		pool4K.Put(bytes)
	} else if size == s8K {
		pool8K.Put(bytes)
	} else if size == s16K {
		pool16K.Put(bytes)
	} else if size == s32K {
		pool32K.Put(bytes)
	} else if size == s64K {
		pool64K.Put(bytes)
	} else if size == s128K {
		pool128K.Put(bytes)
	} else if size == s256K {
		pool256K.Put(bytes)
	} else if size == s512K {
		pool512K.Put(bytes)
	} else if size == s1M {
		pool1M.Put(bytes)
	} else if size == s2M {
		pool2M.Put(bytes)
	} else if size == s4M {
		pool4M.Put(bytes)
	} else if size == s8M {
		pool8M.Put(bytes)
	}
}
